/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server.votesending.xmlsinger;
// kod z tej strony : http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/
// tylko go przerobiłem, żeby móc podpisywać
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.jdom.*;
import org.jdom.input.DOMBuilder;
import org.jdom.output.DOMOutputter;
import org.w3c.dom.NodeList;
/**
 *
 * @author Maciek
 */
public class XmlDSASigner {

    /**
     * @param args the command line arguments
     */
    
    XMLSignatureFactory fac;
    Reference ref ;
    SignedInfo si;
    KeyStore.PrivateKeyEntry keyEntry;
    KeyInfo ki;
    
    public void initXml() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException{
        fac = XMLSignatureFactory.getInstance("DOM");
        ref = fac.newReference ("", fac.newDigestMethod(DigestMethod.SHA1, null),Collections.singletonList
               (fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
        si = fac.newSignedInfo
             (fac.newCanonicalizationMethod
              (CanonicalizationMethod.INCLUSIVE,
               (C14NMethodParameterSpec) null),
                fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
                 Collections.singletonList(ref));
    }
    
    public void initKeys(String keystore, String storepass, String keypass, String alias) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException{
            
                    KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream(keystore), storepass.toCharArray());
        keyEntry =
            (KeyStore.PrivateKeyEntry) ks.getEntry
                (alias, new KeyStore.PasswordProtection(keypass.toCharArray()));
        X509Certificate cert = (X509Certificate) keyEntry.getCertificate();

        // Create the KeyInfo containing the X509Data.
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        List x509Content = new ArrayList();
        x509Content.add(cert.getSubjectX500Principal().getName());
        x509Content.add(cert);
        X509Data xd = kif.newX509Data(x509Content);
        ki = kif.newKeyInfo(Collections.singletonList(xd));
    }
    
    public org.w3c.dom.Document xmlSign(Object object) throws JDOMException, MarshalException, XMLSignatureException{
        Element element = XMLParser.parseToXmlSimpleObject(object);
        Document document = new Document(element);
        org.w3c.dom.Document domDocument = convertToDOM(document);
        // Create a DOMSignContext and specify the RSA PrivateKey and
        // location of the resulting XMLSignature's parent element.
        DOMSignContext dsc = new DOMSignContext (keyEntry.getPrivateKey(), domDocument.getDocumentElement());

        // Create the XMLSignature, but don't sign it yet.
        XMLSignature signature = fac.newXMLSignature(si, ki);

        // Marshal, generate, and sign the enveloped signature.
        signature.sign(dsc);
        return domDocument;
    }
    
    public void writeSigned(OutputStream os, org.w3c.dom.Document  domDocument) throws TransformerConfigurationException, TransformerException {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();
        trans.transform(new DOMSource(domDocument), new StreamResult(os));
    }
    
    public boolean validateSignature(org.w3c.dom.Document domDocument) throws Exception{
        // Find Signature element.
        NodeList nl =
            domDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
        if (nl.getLength() == 0) {
            throw new Exception("Cannot find Signature element");
        }

        // Create a DOMValidateContext and specify a KeySelector
        // and document context.
        DOMValidateContext valContext = new DOMValidateContext
            (new X509KeySelector(), nl.item(0));
        
        
        XMLSignature signature = signature = fac.unmarshalXMLSignature(valContext);
        // Unmarshal the XMLSignature.


        // Validate the XMLSignature.

        boolean coreValidity = signature.validate(valContext);

        // Check core validation status.
        if (coreValidity == false) {
            System.err.println("Signature failed core validation");
            boolean sv = signature.getSignatureValue().validate(valContext);
            System.out.println("signature validation status: " + sv);
            if (sv == false) {
                // Check the validation status of each Reference.
                Iterator i = signature.getSignedInfo().getReferences().iterator();
                for (int j=0; i.hasNext(); j++) {
                    boolean refValid = ((Reference) i.next()).validate(valContext);
                    System.out.println("ref["+j+"] validity status: " + refValid);
                }
            }
        } else {
            System.out.println("Signature passed core validation");
        }
        return coreValidity;
    }
    
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyStoreException, IOException, CertificateException, UnrecoverableEntryException, JDOMException, MarshalException, XMLSignatureException, TransformerConfigurationException, TransformerException, Exception {
                 // Example ;
                 XmlDSASigner signer = new XmlDSASigner();
                 signer.initXml();
                 signer.initKeys("certs" + File.separatorChar + "mycakeystore.jks", "bskbsk", "bskbsk", "cacert");
                 Vote pd = new Vote("halo", "halo");
                 
                 org.w3c.dom.Document signedDocument = signer.xmlSign(pd);
                   NodeList childNodes = signedDocument.getChildNodes();
        int n = childNodes.getLength();
        for(int i = 0 ; i < n ; ++i){
            System.out.println(childNodes.item(i).toString());
            
        }
                 XmlDSASigner validator = new XmlDSASigner();
                 validator.initXml();
                 System.out.println("Noramninie : ");
                 validator.validateSignature(signedDocument);
                 validator.writeSigned(new FileOutputStream("beforeVal.xml"), signedDocument);
                 
                 
                 System.out.println("Konwertuje na jdom i z powrotem");
                 DOMBuilder builder = new DOMBuilder(); 
                 Document document = builder.build(signedDocument);
                 
                 org.w3c.dom.Document nsigedDocument = convertToDOM(document);
                 System.out.println("Po konwersji. Nie powiedzie się ale, jak sobie porównacie pliki to będą takie same :");
                 validator.validateSignature(nsigedDocument);
                 
                 
                 validator.writeSigned(new FileOutputStream("afterVal.xml"), nsigedDocument);
        // Ale zapisane do pliku są takie same. Struktura pliku musiała się zmienić
                 NodeList elementsByTagName = signedDocument.getElementsByTagName("firstName");
                 
                 elementsByTagName.item(0).setTextContent("Rambo");
                 System.out.println("Zmieniłem imie. Teraz powinno się nie powieść validacja");
                 validator.validateSignature(signedDocument);
                                 
                 
    }
    
    public static org.w3c.dom.Document convertToDOM(org.jdom.Document jDocument)
     throws JDOMException {
         DOMOutputter outputter = new DOMOutputter();
         return outputter.output(jDocument);
    }
    


    
                // Load the KeyStore and get the signing key and certificate.

    
}
